/////////////////////////////////////////////////////////////////////////////////
// Math Library : Vector 3													   //
// Purpose: A templated class for use of a vector with 3 components			   //
// Author: Roberto Rubiano													   //
/////////////////////////////////////////////////////////////////////////////////

#ifndef VECTOR3_H
#define VECTOR3_H

#include <math.h>

// Templated Class
template<typename type>
struct Vector3
{
	///////////////////////////////////////////////////////////
	// member variables										 //
	///////////////////////////////////////////////////////////
	union
	{
		// array to easily access the 3 components   
		type v[3];

		struct
		{
			type x;
			type y;
			type z;
		};
	};

	// Constructor
	Vector3() {}
	// Destructor
	~Vector3() {}
	// Copy Constructor
	template<typename type>
	Vector3(const Vector3<type>& vector) : x(vector.x), y(vector.y), z(vector.z) {}

	// Constructor to take in one type
	Vector3(type Component) : x(Component), y(Component), z(Component) {}
	// Constructer to take in three types
	Vector3(type ComponentX, type ComponentY, type ComponentZ) : x(ComponentX), y(ComponentY), z(ComponentZ) {}
	
	//////////////////////////
	// Vector Functions     //
	//////////////////////////

	//////////////////////////////////////////////////////
	// Function : type magnitude(void)					//
	// Purpose : return the magnitude of the vector	    //
	//////////////////////////////////////////////////////
	template<typename type>
	type magnitude(void)
	{
		return (type)sqrt( (x*x) + (y*y) + (z*z) );
	}

	////////////////////////////////////////
	// Function : void normalize(void)    //
	// Purpose : normalizes this vector   //
	////////////////////////////////////////
	template<typename type>
	void normalize(void)
	{
		type mag = magnitude();

		x *= mag;
		y *= mag;
		z *= mag;
	}

	//////////////////////////////////////
	// Function : void MakeZero(void)   //
	// Purpose : zeros out this vector  //
	//////////////////////////////////////
	template<typename type>
	void MakeZero(void)
	{
		x = y = z = (type)0;
	}

	//////////////////////////////////////
	// Function : void Negate(void)     //
	// Purpose : negates this vector	//
	//////////////////////////////////////
	template<typename type>
	void Negate(void)
	{
		x = -x;
		y = -y;
		z = -z;
	}

	///////////////////////////////////////////////////////////////////////////////////
	// Function : friend type dot_product(Vector3<type> vec1, Vector3<type> vec2);   //
	// Purpose: calculate the dot product between two vectors                        //
	// Extra : friend function so it can be called from anywhere				     //
	///////////////////////////////////////////////////////////////////////////////////
	friend type Dot_Product(Vector3<type> vec1, Vector3<type> vec2)
	{
		return (vec1.x * vec2.x) + (vec1.y * vec2.y) + (vec1.z * vec2.z);
	}

	//////////////////////////////////////////////////////////////////////////////////////////////////////
	// Function : friend void cross_product(Vector3<type> out, Vector3<type> vec1, Vector3<type> vec2)  //
	// Purpose : calculate the cross product between two vectors                                        //
	// Extra : friend function so it can be called anywhere, will automatically set the return vector   //
	//////////////////////////////////////////////////////////////////////////////////////////////////////
	friend void Cross_Product(Vector3<type> out, Vector3<type> vec1, Vector3<type> vec2)
	{
		out.x = (vec1.y * vec2.z) - (vec1.z * vec2.y);
		out.y = (vec1.z * vec2.x) - (vec1.x * vec2.z);
		out.z = (vec1.x * vec2.y) - (vec1.y * vec2.x);
	}

	/////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Function : friend void cross_product(Vector3<type> out, Vector3<type> vec1, Vector3<type> vec2)         //
	// Purpose : calculate the cross product between two vectors                                               //
	// Extra : friend function so it can be called anywhere, returns a vector of the crossed vectors passed in //
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////
	friend Vector3<type> Cross_Product(Vector3<type> vec1, Vector3<type> vec2)
	{
		Vector3<type> out;

		out.x = (vec1.y * vec2.z) - (vec1.z * vec2.y);
		out.y = (vec1.z * vec2.x) - (vec1.x * vec2.z);
		out.z = (vec1.x * vec2.y) - (vec1.y * vec2.x);

		return out;
	}

	///////////////////////////////////////////////////
	// Overloaded Operators							 //
	///////////////////////////////////////////////////

	// Assignment Operator
	template<typename type>
	void operator=(Vector3<type> vector)
	{
		x = vector.x;
		y = vector.y;
		z = vector.z;
	}

	///////////////////////////////////////////////////
	// Vector to Vector comparision operators		 //
	///////////////////////////////////////////////////
	template<typename type>
	bool operator==(Vector3<type> vector)
	{
		if(x == vector.x && y == vector.y && z == vector.z)
			return true;

		return false;
	}

	template<typename type>
	bool operator>(Vector3<type> vector)
	{
		if(x > vector.x && y > vector.y && z > vector.z)
			return true;

		return false;
	}

	template<typename type>
	bool operator<(Vector3<type> vector)
	{
		if(x < vector.x && y < vector.y && z < vector.z)
			return true;

		return false;
	}

	template<typename type>
	bool operator>=(Vector3<type> vector)
	{
		if(x > vector.x && y > vector.y && z > vector.z)
			return true;
		else if(x == vector.x && y == vector.y && z == vector.z)
			return true;

		return false;
	}

	template<typename type>
	bool operator<=(Vector3<type> vector)
	{
		if(x < vector.x && y < vector.y && z < vector.z)
			return true;
		else if(x == vector.x && y == vector.y && z == vector.z)
			return true;

		return false;
	}

	////////////////////////////////////////////////////////////
	// Vector to Vector math operators						  //
	////////////////////////////////////////////////////////////
	template<typename type>
	Vector3<type> operator+(Vector3<type> vector)
	{
		Vector3<type> add;

		add.x = x + vector.x;
		add.y = y + vector.y;
		add.z = z + vector.z;

		return add;
	}

	template<typename type>
	void operator+=(Vector3<type> vector)
	{
		x += vector.x;
		y += vector.y;
		z += vector.z;
	}

	template<typename type>
	Vector3<type> operator-(Vector3<type> vector)
	{
		Vector3<type> sub;

		sub.x = x - vector.x;
		sub.y = y - vector.y;
		sub.z = z - vector.z;

		return sub;
	}

	template<typename type>
	void operator-=(Vector3<type> vector)
	{
		x -= vector.x;
		y -= vector.y;
		z -= vector.z;
	}

	template<typename type>
	Vector3<type> operator*(Vector3<type> vector)
	{
		Vector3<type> mul;

		mul.x = x * vector.x;
		mul.y = y * vector.y;
		mul.z = z * vector.z;

		return mul;
	}

	template<typename type>
	void operator*=(Vector3<type> vector)
	{
		x *= vector.x;
		y *= vector.y;
		z *= vector.z;
	}

	template<typename type>
	Vector3<type> operator/(Vector3<type> vector)
	{
		Vector3<type> div;

		div.x = x / vector.x;
		div.y = y / vector.y;
		div.z = z / vector.z;

		return div;
	}

	template<typename type>
	void operator/=(Vector3<type> vector)
	{
		x /= vector.x;
		y /= vector.y;
		z /= vector.z;
	}

	//////////////////////////////////////////////////////////
	// Vector to "type" math operations						//
	//////////////////////////////////////////////////////////
	template<typename type>
	Vector3<type> operator+(type item)
	{
		Vector3<type> add;

		add.x = x + item;
		add.y = y + item;
		add.z = z + item;

		return add;
	}

	template<typename type>
	void operator+=(type item)
	{
		x += item;
		y += item;
		z += item;
	}

	template<typename type>
	Vector3<type> operator-(type item)
	{
		Vector3<type> sub;

		sub.x = x - item;
		sub.y = y - item;
		sub.z = z - item;

		return sub;
	}

	template<typename type>
	void operator-=(type item)
	{
		x -= item;
		y -= item;
		z -= item;
	}

	template<typename type>
	Vector3<type> operator*(type item)
	{
		Vector3<type> mul;

		mul.x = x * item;
		mul.y = y * item;
		mul.z = z * item;

		return mul;
	}

	template<typename type>
	void operator*=(type item)
	{
		x *= item;
		y *= item;
		z *= item;
	}

	template<typename type>
	Vector3<type> operator/(type item)
	{
		Vector3<type> div;

		div.x = x / item;
		div.y = y / item;
		div.z = z / item;

		return div;
	}

	template<typename type>
	void operator/=(type item)
	{
		x /= item;
		y /= item;
		z /= item;
	}

};

typedef Vector3<float> vec3f;

#endif